package debtbundler.domain.bll.solver;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import debtbundler.domain.bll.Utils;
import debtbundler.domain.bll.solver.ClosestMatchSolver;
import debtbundler.domain.bll.solver.LinearSystemSolver;
import debtbundler.domain.bll.solver.SimpleSolver;
import debtbundler.domain.entity.Debt;
import debtbundler.domain.entity.Payment;
import debtbundler.domain.entity.SharedCost;
import debtbundler.domain.entity.Person;
import debtbundler.domain.entity.Share;
import debtbundler.domain.entity.impl.EntityManager;

public class SolverTest {
	private static Logger logger = Logger.getLogger(SolverTest.class.getName());
	private Random generator = new Random();
	
	@BeforeClass 
	public static void enableLogging() {
		debtbundler.logging.ConfigurationReader.setDefaultConf();
	}

	@Before
	public void setUp() throws Exception {
	}

	@After
	public void tearDown() throws Exception {
	}
	
	@Test
	public void testBalanceByEqualSharing() {
		Person anssi = EntityManager.createPerson();
		anssi.setName("Anssi");
		Person antti = EntityManager.createPerson();
		antti.setName("Antti");
		Person johannes = EntityManager.createPerson();
		johannes.setName("Johannes");
		Person tomi = EntityManager.createPerson();
		tomi.setName("Tomi");
		Person valtteri = EntityManager.createPerson();
		valtteri.setName("Valtteri");
		
		HashSet<Person> participants = new HashSet<Person>();
		participants.add(anssi);
		participants.add(antti);
		participants.add(johannes);
		participants.add(tomi);
		participants.add(valtteri);
		
		SharedCost safkat = EntityManager.createSharedCost();
		safkat.setName("Safkat");

		Payment anssinSafkaostos = EntityManager.createPayment(anssi, safkat);
		anssinSafkaostos.setValue(100);	
		Payment valtterinSafkaostos = EntityManager.createPayment(valtteri, safkat);
		valtterinSafkaostos.setValue(50);		
		
		Utils.balanceByEqualSharing(safkat, participants);
		
		for (Payment payment : safkat.getPayments()) {
			if (payment.getPerson().equals(anssi)) {
				assertEquals("Payments must be not be changed by equal sharing", 100, payment.getValue(), 0.0);
			} else if (payment.getPerson().equals(valtteri)) { 
				assertEquals("Payments must be not be changed by equal sharing", 50, payment.getValue(), 0.0);
			} else {
				fail();
			}
		}
		
		for (Share share : safkat.getShares()) {
			assertEquals("Costs shared equally", 30.0, share.getValue(), 0.0);
		}
		assertEquals("Balance of shared cost must be zero after equal sharing", 0, safkat.getBalance(), 0.0);
		
		assertEquals("Correct cost balance of person after sharing", -70.0, anssi.getCostBalance(), 0.0);
		assertEquals("Correct cost balance of person after sharing", -20.0, valtteri.getCostBalance(), 0.0);
		assertEquals("Correct cost balance of person after sharing", 30.0, antti.getCostBalance(), 0.0);
		assertEquals("Correct cost balance of person after sharing", 30.0, johannes.getCostBalance(), 0.0);
		assertEquals("Correct cost balance of person after sharing", 30.0, tomi.getCostBalance(), 0.0);
	}
	
	/**
	 * The simplest test for <tt>SimpleSolver</tt>.
	 */
	@Test
	public void oneDebtorOneCreditorSimpleSolver() throws Exception {
		Person creditor = EntityManager.createPerson();
		Payment payment = EntityManager.createPayment(creditor, EntityManager.createSharedCost());
		payment.setValue(37);
		
		Person debtor = EntityManager.createPerson();
		Share share = EntityManager.createShare(debtor, EntityManager.createSharedCost());
		share.setValue(37);
		
		Set<Person> input = new HashSet<Person>();
		input.add(creditor);
		input.add(debtor);
		
		SimpleSolver ss = new SimpleSolver(input);
		assertEquals(1, ss.getDebts().size());
		Debt debt = ss.getDebts().iterator().next();
		assertEquals(debtor, debt.getDebtor());
		assertEquals(creditor, debt.getCreditor());
		assertEquals(37, debt.getValue(), 0);
		assertEquals(0, debtor.getBalance(), 0);
		assertEquals(0, creditor.getBalance(), 0);
		
	}

	/**
	 * The simplest test for <tt>LinearSystemSolver</tt>.
	 */
	@Test
	public void oneDebtorOneCreditorLinearSystemSolver() throws Exception {
		Person creditor = EntityManager.createPerson();
		Payment payment = EntityManager.createPayment(creditor, EntityManager.createSharedCost());
		payment.setValue(37);
		
		Person debtor = EntityManager.createPerson();
		Share share = EntityManager.createShare(debtor, EntityManager.createSharedCost());
		share.setValue(37);
		
		Set<Person> input = new HashSet<Person>();
		input.add(creditor);
		input.add(debtor);
		
		LinearSystemSolver lss = new LinearSystemSolver(input);
		assertEquals(1, lss.getDebts().size());
		Debt debt = lss.getDebts().iterator().next();
		assertEquals(debtor, debt.getDebtor());
		assertEquals(creditor, debt.getCreditor());
		assertEquals(37, debt.getValue(), 0);
		assertEquals(0, debtor.getBalance(), 0);
		assertEquals(0, creditor.getBalance(), 0);
		
	}	
	
	/**
	 * The simplest test for <tt>AllCombinationsSolver</tt>.
	 */
	@Test
	public void oneDebtorOneCreditorAllCombinationsSolver() throws Exception {
		Person creditor = EntityManager.createPerson();
		Payment payment = EntityManager.createPayment(creditor, EntityManager.createSharedCost());
		payment.setValue(37);
		
		Person debtor = EntityManager.createPerson();
		Share share = EntityManager.createShare(debtor, EntityManager.createSharedCost());
		share.setValue(37);
		
		Set<Person> input = new HashSet<Person>();
		input.add(creditor);
		input.add(debtor);
		
		AllCombinationsSolver acs = new AllCombinationsSolver(input);
		assertEquals(1, acs.getDebts().size());
		Debt debt = acs.getDebts().iterator().next();
		assertEquals(debtor, debt.getDebtor());
		assertEquals(creditor, debt.getCreditor());
		assertEquals(37, debt.getValue(), 0);
		assertEquals(0, debtor.getBalance(), 0);
		assertEquals(0, creditor.getBalance(), 0);
		
	}	
	
	/**
	 * The simplest test for <tt>HybridSolver</tt>. Should run with one iteration.
	 */
	@Test
	public void oneDebtorOneCreditorHybridSolver() throws Exception {
		Person creditor = EntityManager.createPerson();
		Payment payment = EntityManager.createPayment(creditor, EntityManager.createSharedCost());
		payment.setValue(37);
		
		Person debtor = EntityManager.createPerson();
		Share share = EntityManager.createShare(debtor, EntityManager.createSharedCost());
		share.setValue(37);
		
		Set<Person> input = new HashSet<Person>();
		input.add(creditor);
		input.add(debtor);
		
		HybridSolver acs = new HybridSolver(input);
		assertEquals(1, acs.getDebts().size());
		Debt debt = acs.getDebts().iterator().next();
		assertEquals(debtor, debt.getDebtor());
		assertEquals(creditor, debt.getCreditor());
		assertEquals(37, debt.getValue(), 0);
		assertEquals(0, debtor.getBalance(), 0);
		assertEquals(0, creditor.getBalance(), 0);
		
	}	
	
	@Test
	public void testAllCombinationsSolverWithRandomData() {
		int numPersons = 4;
		Set<Person> input = createRandomizedData(numPersons);
		
		long st = System.currentTimeMillis();
		new AllCombinationsSolver(input);
		long et = System.currentTimeMillis();
		logger.log(Level.INFO, "Input with {0} persons took {1} ms", new Object[]{numPersons, (et-st)});
		
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
	}
	
	@Test
	public void testHybridSolverWithRandomData() {
		int numPersons = 4;
		Set<Person> input = createRandomizedData(numPersons);
		
		long st = System.currentTimeMillis();
		new HybridSolver(input);
		long et = System.currentTimeMillis();
		logger.log(Level.INFO, "Input with {0} persons took {1} ms", new Object[]{numPersons, (et-st)});
		
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
	}	
		
	
	/**
	 * This case is a test for a simple case where there is one 
	 * person who has paid for the costs that shared equally 
	 * by everyone.
	 * 
	 * @throws Exception
	 */
	@Test
	public void testOneDebtorManyCreditorsLinSysSolver() throws Exception {
						
		Person anssi = EntityManager.createPerson();
		anssi.setName("Anssi");
		Person antti = EntityManager.createPerson();
		antti.setName("Antti");
		Person johannes = EntityManager.createPerson();
		johannes.setName("Johannes");
		Person tomi = EntityManager.createPerson();
		tomi.setName("Tomi");
		Person valtteri = EntityManager.createPerson();
		valtteri.setName("Valtteri");
		
		SharedCost safkat = EntityManager.createSharedCost();
		safkat.setName("Safkat");
		SharedCost juomat = EntityManager.createSharedCost();
		juomat.setName("Juomat");
		

		// Anssi is the only one who has paid for the purchases, costs are shared equally  
		Payment anssinSafkaostos = EntityManager.createPayment(anssi, safkat);
		anssinSafkaostos.setValue(100);
		Payment anssinJuomaostos = EntityManager.createPayment(anssi, juomat);
		anssinJuomaostos.setValue(50);
		
		HashSet<Person> participants = new HashSet<Person>();
		participants.add(anssi);
		participants.add(antti);
		participants.add(johannes);
		participants.add(tomi);
		participants.add(valtteri);
		
		Utils.balanceByEqualSharing(safkat, participants);
		Utils.balanceByEqualSharing(juomat, participants);
		
		LinearSystemSolver solver = new LinearSystemSolver(participants);
		List<Debt> debts = solver.getDebts();
		
		// Assert the values of the debts: everyone else owes Anssi a value of 30.00.
		for (Debt debt : debts) {			
			if (debt.getCreditor() != anssi && !(debt.getDebtor().equals(antti) || debt.getDebtor().equals(johannes) || debt.getDebtor() == tomi || debt.getDebtor().equals(valtteri)) ) {
				fail("Discovered an unexpected debt");
			} else {
				assertEquals("Everyone else owes Anssi a value of 30", 30.0, debt.getValue(), 0.001);
			}
		}
		
	}
	
	/**
	 * This case is a more complex debtbundler case where there are more than one 
	 * debtor and more than creditor.
	 * 
	 * @throws Exception
	 */
	@Test	
	public void testManyDebtorsManyCreditors() throws Exception {
		
		Person anssi = EntityManager.createPerson();
		anssi.setName("Anssi");
		Person antti = EntityManager.createPerson();
		antti.setName("Antti");
		Person johannes = EntityManager.createPerson();
		johannes.setName("Johannes");
		Person tomi = EntityManager.createPerson();
		tomi.setName("Tomi");
		Person valtteri = EntityManager.createPerson();
		valtteri.setName("Valtteri");
		
		HashSet<Person> participants = new HashSet<Person>();
		participants.add(anssi);
		participants.add(antti);
		participants.add(johannes);
		participants.add(tomi);
		participants.add(valtteri);
		
		SharedCost safkat = EntityManager.createSharedCost();
		safkat.setName("Safkat");
		SharedCost juomat = EntityManager.createSharedCost();
		juomat.setName("Juomat");
		SharedCost bensat = EntityManager.createSharedCost();
		bensat.setName("Bensat");
		SharedCost pokeri = EntityManager.createSharedCost();
		pokeri.setName("Pokeri");
		
		Payment payment;
		payment = EntityManager.createPayment(anssi, safkat);
		payment.setValue(100);
		payment = EntityManager.createPayment(johannes, safkat);
		payment.setValue(30);
		Utils.balanceByEqualSharing(safkat, participants);
		
		payment = EntityManager.createPayment(tomi, juomat);
		payment.setValue(70);
		payment = EntityManager.createPayment(antti, juomat);
		payment.setValue(30);
		Utils.balanceByEqualSharing(juomat, participants);
		
		payment = EntityManager.createPayment(valtteri, bensat);
		payment.setValue(50);
		Utils.balanceByEqualSharing(bensat, participants);
		
		
		// Poker game with a stack of 40 EUR per person. 
		// Tomi wins 120 EUR, Antti wins 80 EUR. The rest get nothing.
		// The unpaid winnings become costs to the winners.
		Share share = EntityManager.createShare(tomi, pokeri);
		share.setValue(40);
		share = EntityManager.createShare(antti, pokeri);
		share.setValue(40);
		share = EntityManager.createShare(anssi, pokeri);
		share.setValue(40);
		share = EntityManager.createShare(johannes, pokeri);
		share.setValue(40);
		share = EntityManager.createShare(valtteri, pokeri);
		share.setValue(40);
		payment = EntityManager.createPayment(tomi, pokeri);
		payment.setValue(120);
		payment = EntityManager.createPayment(antti, pokeri);
		payment.setValue(80);

		LinearSystemSolver solver = new LinearSystemSolver(participants);
		//SimpleSolver solver = new SimpleSolver(participants);
		
		for (Person person : participants) {
			assertEquals("Balance of " + person.getName() + " must be zero", 0, person.getBalance(), 0.005);
		}
		
		for (Debt debt : solver.getDebts()) {
			System.out.println(debt);
		}
	}
	
	
	/**
	 * In this case the minimum number of debts is six or less:
	 * <ul>
	 * <li>SimpleSolver finds 7 debts if it always matches greatest debts against greatest credits</li>
	 * <li>ClosestMatchSolver finds 6 debts</li>
	 * <li>LinearSystemSolver finds 6 debts</li>
	 * <li>AllCombinationsSolver finds 6 debts</li>
	 * <li>RandomizedSolver finds 6 debts</li>
	 * </ul> 
	 * Note that solvers do not care if individual items are balanced or not. All that
	 * matters is that the payment balances of the persons sum to zero.
	 */
	@Test
	public void testMinimizingDebts1() {
		Set<Person> input = new HashSet<Person>();
		
		Person person = EntityManager.createPerson();
		input.add(person);
		Payment payment = EntityManager.createPayment(person, EntityManager.createSharedCost());
		payment.setValue(21);
		
		person = EntityManager.createPerson();
		input.add(person);
		payment = EntityManager.createPayment(person, EntityManager.createSharedCost());
		payment.setValue(63);
		
		person = EntityManager.createPerson();
		input.add(person);
		payment = EntityManager.createPayment(person, EntityManager.createSharedCost());
		payment.setValue(28);
		
		person = EntityManager.createPerson();
		input.add(person);
		payment = EntityManager.createPayment(person, EntityManager.createSharedCost());
		payment.setValue(241);
		
		person = EntityManager.createPerson();
		input.add(person);
		Share share = EntityManager.createShare(person, EntityManager.createSharedCost());
		share.setValue(28);
		
		person = EntityManager.createPerson();
		input.add(person);
		share = EntityManager.createShare(person, EntityManager.createSharedCost());
		share.setValue(31);
		
		person = EntityManager.createPerson();
		input.add(person);
		share = EntityManager.createShare(person, EntityManager.createSharedCost());
		share.setValue(280);
		
		person = EntityManager.createPerson();
		input.add(person);
		share = EntityManager.createShare(person, EntityManager.createSharedCost());
		share.setValue(14);

		SimpleSolver ss = new SimpleSolver(input);
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
		StringBuilder sb = new StringBuilder("SimpleSolver:\n");
		for (Debt debt : ss.getDebts()) {
			sb.append(debt);
			sb.append("\n");
		}
		logger.log(Level.INFO, sb.toString());
		
		ClosestMatchSolver cms = new ClosestMatchSolver(input);
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
		sb = new StringBuilder("ClosestMatchSolver:\n");
		for (Debt debt : cms.getDebts()) {
			sb.append(debt);
			sb.append("\n");
		}
		logger.log(Level.INFO, sb.toString());
				
		LinearSystemSolver lss = new LinearSystemSolver(input);
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
		sb = new StringBuilder("LinearSystemSolver:\n");
		for (Debt debt : lss.getDebts()) {
			sb.append(debt);
			sb.append("\n");
		}
		logger.log(Level.INFO, sb.toString());
		
		AllCombinationsSolver acs = new AllCombinationsSolver(input);
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
		sb = new StringBuilder("AllCombinationsSolver:\n");
		for (Debt debt : acs.getDebts()) {
			sb.append(debt);
			sb.append("\n");
		}
		logger.log(Level.INFO, sb.toString());
		
		RandomizedSolver rs = new RandomizedSolver(input);
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
		sb = new StringBuilder("RandomizedSolver:\n");
		for (Debt debt : rs.getDebts()) {
			sb.append(debt);
			sb.append("\n");
		}
		logger.log(Level.INFO, sb.toString());
		
	}

	/**
	 * In this case the minimum number of debts is six or less.
	 * <ul>
	 * <li>SimpleSolver finds 7 debts if it always matches greatest debts against greatest credits</li>
	 * <li>ClosestMatchSolver finds 6 debts</li>
	 * <li>LinearSystemSolver finds 6 debts</li>
	 * </ul> 
	 * Note that solvers do not care if individual items are balanced or not. All that
	 * matters is that the payment balances of the persons sum to zero.
	 */
	@Test
	public void testMinimizingDebts2() {
		Set<Person> input = new HashSet<Person>();
		
		Person person = EntityManager.createPerson();
		input.add(person);
		Payment payment = EntityManager.createPayment(person, EntityManager.createSharedCost());
		payment.setValue(145);
		
		person = EntityManager.createPerson();
		input.add(person);
		payment = EntityManager.createPayment(person, EntityManager.createSharedCost());
		payment.setValue(427);
		
		person = EntityManager.createPerson();
		input.add(person);
		payment = EntityManager.createPayment(person, EntityManager.createSharedCost());
		payment.setValue(182);
		
		person = EntityManager.createPerson();
		input.add(person);
		payment = EntityManager.createPayment(person, EntityManager.createSharedCost());
		payment.setValue(107);
		
		person = EntityManager.createPerson();
		input.add(person);
		Share share = EntityManager.createShare(person, EntityManager.createSharedCost());
		share.setValue(580);
		
		person = EntityManager.createPerson();
		input.add(person);
		share = EntityManager.createShare(person, EntityManager.createSharedCost());
		share.setValue(157);
		
		person = EntityManager.createPerson();
		input.add(person);
		share = EntityManager.createShare(person, EntityManager.createSharedCost());
		share.setValue(99);
		
		person = EntityManager.createPerson();
		input.add(person);
		share = EntityManager.createShare(person, EntityManager.createSharedCost());
		share.setValue(25);
		
		
		SimpleSolver ss = new SimpleSolver(input);
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
		StringBuilder sb = new StringBuilder("SimpleSolver:\n");
		for (Debt debt : ss.getDebts()) {
			sb.append(debt);
			sb.append("\n");
		}
		logger.log(Level.INFO, sb.toString());
				
		ClosestMatchSolver cms = new ClosestMatchSolver(input);
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
		sb = new StringBuilder("ClosestMatchSolver:\n");
		for (Debt debt : cms.getDebts()) {
			sb.append(debt);
			sb.append("\n");
		}
		logger.log(Level.INFO, sb.toString());
		
		LinearSystemSolver lss = new LinearSystemSolver(input);
		for (Person p : input) {
			assertEquals("Balance of " + p.getName() + " must be zero", 0, p.getBalance(), 0.005);
		}
		sb = new StringBuilder("LinearSystemSolver:\n");
		for (Debt debt : lss.getDebts()) {
			sb.append(debt);
			sb.append("\n");
		}
		logger.log(Level.INFO, sb.toString());
		
	}	
	
	/**
	 * Creates a set of persons consisting of persons with random (but balanced) amounts
	 * of debt/credit.
	 * 
	 * @param maxPersons	maximum number of debtors or creditors
	 * @return				set of debtors and creditors
	 */
	public Set<Person> createRandomizedData(int maxPersons) {
		
		Set<Person> debtors = new HashSet<Person>();
		Set<Person> creditors = new HashSet<Person>();
		
		double totalCredit = Math.round(generator.nextDouble()*1000.0);
		double totalDebt = totalCredit;
		
		while (totalCredit > 0 && creditors.size() < maxPersons) {
			double creditShare;
			if (creditors.size() < maxPersons-1) { 
				creditShare =  Math.round(generator.nextDouble()*totalCredit);
			} else {
				creditShare = totalCredit;
			}
			Person creditor = EntityManager.createPerson();
			Payment payment = EntityManager.createPayment(creditor, EntityManager.createSharedCost());
			payment.setValue(creditShare);
			creditors.add(creditor);
			totalCredit -= creditShare;
		}
		
		while (totalDebt > 0 && debtors.size() < maxPersons) {
			double debtShare;
			if (debtors.size() < maxPersons-1) { 
				debtShare = Math.round(generator.nextDouble()*totalDebt);
			} else {
				debtShare = totalDebt;
			}
			Person debtor = EntityManager.createPerson();
			Share share = EntityManager.createShare(debtor, EntityManager.createSharedCost());
			share.setValue(debtShare);
			debtors.add(debtor);
			totalDebt -= debtShare;
		}
		
		Set<Person> input = new HashSet<Person>();
		input.addAll(debtors);
		input.addAll(creditors);
		
		return input;
	}
	
	/**
	 * Tests two different solvers with randomized input data.
	 */
	public void randomizedTesting() {
		
		
		final int maxPersons = 5;
		while (true) {
			
			Set<Person> input = createRandomizedData(maxPersons);
			
			Set<Person> debtors = new HashSet<Person>();
			Set<Person> creditors = new HashSet<Person>();
			Utils.findDebtorsAndCreditors(input, debtors, creditors, null);
			
			logger.log(Level.INFO, "Solving system with {0} debtors {1} creditors...", new Object[]{debtors.size(), creditors.size()});
			
			try {
				long st = System.currentTimeMillis();
				AllCombinationsSolver acs = new AllCombinationsSolver(input);
				long et = System.currentTimeMillis();
				logger.log(Level.INFO, "All combinations solution took {0} ms", (et-st));
				
				st = System.currentTimeMillis();
				RandomizedSolver rss = new RandomizedSolver(input);
				et = System.currentTimeMillis();
				logger.log(Level.INFO, "Randomized solution took {0} ms", (et-st));
				
				if (acs.getDebts().size() == rss.getDebts().size()) {
					logger.log(Level.INFO, "Both solvers reported {0} debts", acs.getDebts().size());
				} else {
					StringBuilder sb = new StringBuilder();
					for (Person debtor : debtors) {
						sb.append("Debtor: " + Utils.sum(debtor.getPayments()) + "\n");
					}
					for (Person creditor : creditors) {
						sb.append("Creditor: " + Utils.sum(creditor.getShares()) + "\n");
					}
					
					logger.log(Level.WARNING, "Solvers reported different number of debts!\nAll combinations solution: {0}\nRandomized solution: {1}\n\n"+sb.toString(), new Object[]{acs.getDebts().size(), rss.getDebts().size()});
				}
				
			} catch (Exception e) {
				StringBuilder sb = new StringBuilder();
				for (Person debtor : debtors) {
					sb.append("Debtor: " + debtor.getBalance() + "\n");
				}
				for (Person creditor : creditors) {
					sb.append("Creditor: " + creditor.getBalance() + "\n");
				}
				logger.log(Level.SEVERE, "Error\n"+sb.toString(), e);
			}
			
		}
		
	}
	
	public static void main(String[] args) {
		SolverTest test = new SolverTest();
		test.randomizedTesting();
	}
}
